#region Using directives

using System;
using System.Collections.Generic;
using System.Text;

using Weazel.Math;

#endregion

namespace Weazel.Gezel.Simulator.Reference
{
  public abstract class AbstractIpBlock
  {
    public enum IoDirection { Input, Output };

    private List<AbstractSignal> inputs;
    internal List<AbstractSignal> Inputs
    {
      get { return inputs; }      
    }

    private List<AbstractSignal> outputs;
    internal List<AbstractSignal> Outputs
    {
      get { return outputs; }      
    }
    protected Dictionary<AbstractSignal, uint> ioMap;
    protected long generation;

    protected List<Integer> ioValues;

		private StringBuilder outputBuffer;
		public StringBuilder OutputBuffer
		{
			get { return outputBuffer; }
			internal set { outputBuffer = value; }
		}

    private readonly string name;
    public string Name
    {
      get { return name; }
    }

    public AbstractIpBlock(string name)
    {			
      this.name = name;			
      this.inputs = new List<AbstractSignal>();
      this.outputs = new List<AbstractSignal>();
      this.ioMap = new Dictionary<AbstractSignal, uint>();
      this.ioValues = new List<Integer>();
    }

    internal virtual void addInput(InputSignal i, string terminal, uint wlenreq, uint signreq)
    {
      inputs.Add(i);
      ioValues.Add(new Integer((int)wlenreq, signreq == 1));
      ioMap[i] = (uint)(ioValues.Count - 1);
      if (!CheckTerminal(ioValues.Count - 1, terminal, IoDirection.Input))
      {
				this.outputBuffer.AppendLine("Warning: ipblock terminal name mismatch for input ");
        this.outputBuffer.AppendLine(terminal + " (" + (ioValues.Count - 1) + ")\n");
      }
    }

    internal virtual void addOutput(IpOutputSignal outputSignal, string terminal)
    {
      outputs.Add(outputSignal);
      ioValues.Add(new Integer((int)outputSignal.Width(), outputSignal.Signed() == 1));
      ioMap[outputSignal] = (uint)(ioValues.Count - 1);
      if (!CheckTerminal(ioValues.Count - 1, terminal, IoDirection.Output))
      {
        this.outputBuffer.AppendLine("Warning: ipblock terminal name mismatch for output");
				this.outputBuffer.AppendLine(terminal + " (" + (ioValues.Count - 1) + ")\n");
      }
    }

    public virtual void Evaluate(long g, bool topLevelCall)
    {
      if (generation != g)
      {
				foreach (InputSignal i in inputs)
				{
					ioValues[(int)ioMap[i]] = i.Evaluate(null, g);										
				}

        Run();
        generation = g;

        //if (cannotSleepTest())
        //  Simulation.Instance.glbRTSleep.cannotSleep();

        //eval calls to the outputs is to update their generation count
        foreach (AbstractSignal ass in outputs)
        {									
					Integer j = ass.Evaluate(null, g);					
        }
      }
    }

    internal Integer outValue(AbstractSignal signal)
    {
      if (!outputs.Contains(signal))
      {
        throw new ApplicationException("cannot find ip output");
      }

      int index = (int) ioMap[signal];

      Integer result = ioValues[index];

      return result;
    }

    #region methods which needs to be overridden by implementors

    public abstract void Run();
    public abstract void SetParameter(string _name);
    public abstract bool CheckTerminal(int n, string tname, IoDirection dir);

    #endregion

    public virtual bool CannotSleepTest()
    {
      return true;
    }

    public virtual bool NeedsWakeupTest()
    {
      return false;
    }

    /// <summary>
    /// used by codesign interfaces
    /// </summary>
    public virtual void Touch()
    {
    }
  }
}